/*************************************************************************
	> File Name: Awinic_mec.c
	> Author: 
	> Mail: 
	> Created Time: 2018/1/23 Tue 11:32
	> Version     :
 ************************************************************************/

#include<stdio.h>
#include<stdint.h>
#include<string.h>
#include "AwinicLog.h"
#include"AwinicMec.h"
#include"BaseAlgo.h"


/***********************************************************************************************
 * *Name        :dataCompressTo15Bit
 * *Description :Compress Input Data To 15Bit
 * *Parameter   :inData ,inData number ,dataBitWidth
 * *Return Value: NULL
 * *Version: v1.0.0
 * ********************************************************************************************/
static int dataCompressTo15Bit(int32_t *data,uint16_t dataCnt,uint16_t dataBitWidth)
{
  uint16_t i;
  int16_t compressBit = dataBitWidth - DATA_FORMAT_BIT_NUM;  //Get InPut Data Need Compress Bit number

  if(compressBit < 0){
	  AWLOGD("data bit Low 15 ,Please Check !\n");
      return -1;
  }

  if(compressBit == 0)
  {
	  //AWLOGD("data bit is 15 !\n",);
      return 0;
  }
  for(i=0;i<dataCnt;i++)
  {
      data[i]=int32RightShift(data[i],compressBit);
  }
  return 0;
}
/***********************************************************************************************
 * *Name        :dataMultInitGain
 * *Description :Multiply Initial Gain
 * *Parameter   :inData ,inData number ,initGain
 * *Return Value: NULL
 * *Version: v1.0.0
 * ********************************************************************************************/
static void dataMultInitGain(int32_t *data,int32_t initGain,uint16_t dataCnt)
{
    int i;

    for(i=0;i<dataCnt;i++)
    {
        data[i] = int32Mulint32(data[i],initGain,INIT_EXP_BIT);
    }
}
/***********************************************************************************************
 * *Name        :dataMultScaleGain
 * *Description :Multiply Scale Gain
 * *Parameter   :inData ,inData number ,initGain
 * *Return Value: NULL
 * *Version: v1.0.0
 * ********************************************************************************************/
static void dataMultScaleGain(int32_t *data,int32_t scaleGain,uint16_t dataCnt)
{
    int i;

    for(i=0;i<dataCnt;i++)
    {
        data[i] = int32Mulint32(data[i],scaleGain,SCALE_EXP_BIT);
    }

}
/***********************************************************************************************
 * *Name        :getMinGainValue
 * *Description :Get Mix Value ,Store Data to data1
 * *Parameter   :inData1 ,inData number ,data2
 * *Return Value: NULL
 * *Version: v1.0.0
 * ********************************************************************************************/
static void getMinGainValue(int32_t *data1,int32_t *data2,uint16_t dataCnt)
{
    uint16_t i;
    for(i=0;i<dataCnt;i++)
    {
        data1[i] = GetMinValue(data1[i],data2[i]);
    }
}
/***********************************************************************************************
 * *Name        :dataGetGainProcess
 * *Description :Compute Gain by input Data
 * *Parameter   :Data ,configuration r ,data number
 * *Return Value: NULL
 * *Version: v1.0.0
 * ********************************************************************************************/
static void dataGetGainProcess(int32_t *data,awinicMecCfg_t *cfg,uint16_t dataCnt)
{
   int32_t volDataTmpBuf[TMP_DATA_BUFFER_SIZE];

   memcpy(volDataTmpBuf,data,sizeof(int32_t)*dataCnt);

   //MembraneExcursionLimiterProcess
   memExLimiterProcess(data,&cfg->memEx_cfg,dataCnt);
   //write_pcm("../data/memGain.pcm",data,dataCnt);

   //Voltage Limiter Process
   volLimiterProcess(volDataTmpBuf,&cfg->vol_cfg,dataCnt);
   //write_pcm("../data/volgGin.pcm",volDataTmpBuf,dataCnt);

   //Get Min Gain
   getMinGainValue(data,volDataTmpBuf,dataCnt);
   //write_pcm("../data/minGain.pcm",data,dataCnt);
}
/***********************************************************************************************
 * *Name        :dataShiftProcess
 * *Description :Move Data to Shift buffer And move Shift buffer data to dataBuf
 * *Parameter   :Data ,configuration  ,data number
 * *Return Value: NULL
 * *Version: v1.0.0
 * ********************************************************************************************/
static void dataShiftProcess(int32_t *data,awinicMecCfg_t *cfg,uint16_t dataCnt)
{
    int shiftCnt = cfg->shiftCnt;
    int32_t *shiftBuf = cfg->shiftGainBuf;
    uint16_t i,dataSizeCnt;
    int32_t dataTmpBuf[TMP_DATA_BUFFER_SIZE]={0};
    int32_t *tmpDataPtr;
   
    //return;
    //AWLOGD(" shift cnt is %d \n",shiftCnt);
    //AWLOGD(" data cnt is %d \n",dataCnt);
    if(dataCnt >= shiftCnt)
    {
        //move shift_buf data to dataTmpBuf 
        for(i=0;i<shiftCnt;i++)
        {
            dataTmpBuf[i] = shiftBuf[i];
        }
        //move current data first (dataCnt - shiftCnt) data to dataTmpBuf
        tmpDataPtr = dataTmpBuf + shiftCnt;
        dataSizeCnt = dataCnt - shiftCnt;
        //AWLOGD(" dataCnt - shiftCnt is %d \n",dataSizeCnt);
        for(i=0;i<dataSizeCnt;i++)
        {
            tmpDataPtr[i] = data[i];
        }
        //move current data_buf data(last shiftCnt number) to shiftBuf
        tmpDataPtr = data + dataSizeCnt;
        for(i=0;i<shiftCnt;i++)
        {
            shiftBuf[i] = tmpDataPtr[i];
        }
        //move dataTmpBuf data to current data_buf
        for(i=0;i<dataCnt;i++)
        {
            data[i] = dataTmpBuf[i];
        }

    }else{
        //move shiftBuf fisrt datacnt data to dataTmpBuf
        for(i=0;i<dataCnt;i++)
        {
            dataTmpBuf[i] = shiftBuf[i];
        }
        //move remain data to shiftBuf initial postion
        tmpDataPtr = shiftBuf + dataCnt;
        dataSizeCnt = shiftCnt - dataCnt;
        for(i=0;i<dataSizeCnt;i++)
        {
            shiftBuf[i] = tmpDataPtr[i]; 
        }
        //move current data to shiftBuf 
        tmpDataPtr = shiftBuf + dataSizeCnt;
        for(i=0;i<dataCnt;i++)
        {
            tmpDataPtr[i] = data[i];
        }
        //move dataTmpBuf data to current data buf
        for(i=0;i<dataCnt;i++)
        {
            data[i] = dataTmpBuf[i];
        }
    }
}
/***********************************************************************************************
 * *Name        :dataMultiGain
 * *Description :Data Multiply Gain
 * *Parameter   :Databuffer ,Gainbuffer  ,data number
 * *Return Value: NULL
 * *Version: v1.0.0
 * ********************************************************************************************/
static void dataMultiGain(int32_t *data,int32_t *gain,uint16_t dataCnt)
{
    int i;

    for(i=0;i<dataCnt;i++)
    {
        data[i] = int32Mulint32(data[i],gain[i],GainData_EXP_BIT);
    }
}
/***********************************************************************************************
 * *Name        :awinicMecProcess
 * *Description :awinic mec process
 * *Parameter   :Data ,configuration  ,data number
 * *Return Value: NULL
 * *Version: v1.0.0
 * ********************************************************************************************/
static  void awinicMecProcess(int32_t *data,awinicMecCfg_t *cfg,uint16_t dataCnt)
{
    int32_t tmpDataBuf[TMP_DATA_BUFFER_SIZE];
    int ret;

    memcpy(tmpDataBuf,data,sizeof(int32_t)*dataCnt);

    //Before Process Compress Indata to 15 Bit
    ret = dataCompressTo15Bit(tmpDataBuf,dataCnt,cfg->dataBitNum);
    if(ret<0)
        return;
    //Multiply InitGain
    //AWLOGD("InitGain is %d \n",cfg->initGain);
    dataMultInitGain(tmpDataBuf,cfg->initGain,dataCnt);
    //write_pcm("../data/initGain.pcm",tmpDataBuf,dataCnt);
    //Multiply ScaleGain
    //AWLOGD("ScaleGain is %d \n",cfg->scaleGain);
    dataMultScaleGain(tmpDataBuf,cfg->scaleGain,dataCnt);
    //write_pcm("../data/scaleGain.pcm",tmpDataBuf,dataCnt);
    //algorithm process get GainValue
    dataGetGainProcess(tmpDataBuf,cfg,dataCnt);
    //write_pcm("../data/gain.pcm",tmpDataBuf,dataCnt);
    //Get Data shift process
    dataShiftProcess(data,cfg,dataCnt);
    //origin data Multiply GetGainValue
    dataMultiGain(data,tmpDataBuf,dataCnt);
    //write_pcm("../data/multiGain.pcm",data,dataCnt);
    // origin data Multiply InitGain
    dataMultInitGain(data,cfg->initGain,dataCnt);
    //write_pcm("../data/dataMultInitGain.pcm",data,dataCnt);

}

void awinicAlgoProcess32Bit(int32_t *data_ptr,awinicMecCfg_t *cfg,uint16_t dataCnt)
{
    if(data_ptr == NULL || cfg == NULL || dataCnt == 0)
    { 
        //AW_LOGD("%s: Error Pointer or data is NULL \n",__func__);
        return;
    }
   
    awinicMecProcess(data_ptr,cfg,dataCnt);
        
}
void awinicAlgoProcess16Bit(int16_t *data_ptr,awinicMecCfg_t *cfg,uint16_t dataCnt)
{
    int32_t dataTmpBuf[TMP_DATA_BUFFER_SIZE];
    uint16_t  i;
    //AWLOGD(" dataCnt %d \n",dataCnt);
    if(data_ptr == NULL || cfg == NULL || dataCnt == 0)
    { 
    	AWLOGE(" Pointer or data is NULL \n");
        return;
    }
    //Copy 16Bit to 32bit BUffer
    //write_pcm16bit("../data/indata16.pcm",data_ptr,dataCnt);
    for(i=0;i<dataCnt;i++)
    {
        dataTmpBuf[i] = data_ptr[i];
    }
    //write_pcm("../data/indata32.pcm",dataTmpBuf,dataCnt);
    //Algo Process
    awinicMecProcess(dataTmpBuf,cfg,dataCnt);

    //Copy processed data to 16Bit buffer
    for(i=0;i<dataCnt;i++)
    {
        data_ptr[i] = dataTmpBuf[i] ;
    }
}
int awinicAlgoInit(awinicMecCfg_t *cfg)
{
    int ret ;

    ret = memExLimiterInit(&cfg->memEx_cfg);
    if(ret < 0)
        return ret;

    ret = voltageLimiterInit(&cfg->vol_cfg);
    
    return ret;
}
int awinicAlgoReset(awinicMecCfg_t *cfg)
{
    int ret ;

    ret = memExLimiterReset(&cfg->memEx_cfg);
    if(ret < 0)
        return ret;

    ret = voltageLimiterReset(&cfg->vol_cfg);
    
    return ret;
}
int awinicAlgoSetParams(awinicMecCfg_t *cfg,awinicMecParams_t *params)
{
	int ret=0;
	if(params->shiftCnt > SHIFT_BUFFER_SIZE)
	{
		AWLOGE(" shift num error\n");
		return -1;
	}
    cfg->initGain  = params->initGain;
    cfg->scaleGain = params->scaleGain;
    cfg->shiftCnt  = params->shiftCnt;

    ret=memExLimiterSetParams(&cfg->memEx_cfg,&params->memEx_params,params->xmaxThreshold);
    if(ret<0)
    {
		AWLOGE(" Mem Set Params %d\n",ret);
		return -1;
    }

    volLimiterParams_t vol_params;
    vol_params.pd_params = params->memEx_params.pd_params;
    vol_params.gcur_params = params->memEx_params.gcur_params;
    vol_params.gs_params = params->memEx_params.gs_params;

    voltageLimiterSetParams(&cfg->vol_cfg,&vol_params,params->vmaxThreshold);

    return 0;
}

int awinicAlgoGetParams(awinicMecCfg_t *cfg,awinicMecParams_t *params)
{
    params->initGain  = cfg->initGain  ;
    params->scaleGain = cfg->scaleGain ;
    params->shiftCnt  = cfg->shiftCnt  ;

    memExLimiterGetParams(&cfg->memEx_cfg,&params->memEx_params,&params->xmaxThreshold);

    volLimiterParams_t vol_params;

    voltageLimiterGetParams(&cfg->vol_cfg,&vol_params,&params->vmaxThreshold);

    return 0;
}
